package com.beiding.hotcode.v2;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

class RealClass implements MyClass<Object> {

    private Class type;

    RealClass(Class type) {
        this.type = type;
        init();
    }

    private Map<String, Map<Integer, List<MethodHolder>>> methods = new HashMap<>();

    private void init() {
        for (Method method : type.getMethods()) {

            //只处理静态方法
            if (Modifier.isStatic(method.getModifiers())) {
                Class<?>[] parameterTypes = method.getParameterTypes();
                Map<Integer, List<MethodHolder>> integerListMap = methods.computeIfAbsent(method.getName(), k -> new HashMap<>());
                List<MethodHolder> methodHolders = integerListMap.computeIfAbsent(parameterTypes.length, k -> new ArrayList<>());
                MethodHolder methodHolder = new MethodHolder(method, parameterTypes);
                methodHolders.add(methodHolder);
            }
        }
    }

    private MethodHolder getMethod(String method, Object... params) {
        Map<Integer, List<MethodHolder>> integerListMap = methods.get(method);
        if (integerListMap == null) {
            return null;
        }
        List<MethodHolder> methodHolders = integerListMap.get(params.length);
        if (methodHolders == null) {
            return null;
        }

        MethodHolder m = null;
        for (MethodHolder methodHolder : methodHolders) {
            if (methodHolder.match(params)) {
                if (m != null) {
                    throw new RuntimeException("无法确定方法:" + method + "(" + createParamTypesString(params) + ")");
                }
                m = methodHolder;
            }
        }
        if (m == null) {
            throw new RuntimeException("未找到方法:" + method + "(" + createParamTypesString(params) + ")");
        }
        return m;
    }


    private String createParamTypesString(Object[] params) {

        StringBuilder builder = new StringBuilder();

        if (params.length > 0) {
            builder.append(params[0] == null ? "null" : params[0].getClass().getName());
            for (int i = 1; i < params.length; i++) {
                builder.append(",").append(params[i] == null ? "null" : params[i].getClass().getName());
            }
        }
        return builder.toString();
    }

    @Override
    public Class getType() {
        return type;
    }


    public Object invokeCatch(String method, Object... params) throws Throwable {

        try {
            MethodHolder m = getMethod(method, params);
            if (m == null) {
                throw new NoSuchMethodException(method + "(" + createParamTypesString(params) + ")");
            }
            return m.getMethod().invoke(null, params);
        } catch (InvocationTargetException e) {

            //抛出所有异常
            throw e.getTargetException();

        } catch (Exception e) {

            throw new RuntimeException(e);
        }
    }


    public void set(String field, Object value) {
        try {
            Field f = type.getField(field);
            f.set(null, value);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    public Object get(String field) {
        try {
            Field f = type.getField(field);
            return f.get(null);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public MyInstance newInstance(Object... params) {
        return new RealInstance(this, this.type, params);
    }

    @Override
    public <T> MyMethod<T> method(String name, Class<T> type) {
        return params -> {
            return (T) RealClass.this.invokeCatch(name, params);
        };
    }
}
